﻿using System;
using System.Dynamic;
using System.Linq.Expressions;

namespace InDepth2Demo.Chapter1314
{
    // Learn Hard
    internal class DynamicDemo_LH
    {
        public static void Call()
        {
            object obj = 10;
            //obj = obj + 10; // 出现编译错误
            dynamic i = 10; // 动态类型定义
            i = i + 10;

            // 在以上代码中，第一行的obj为object类型，而编译器却检测出 “+” 运算符无法应用于 object 和 int 类型。
            // 要让代码编译通过，我们必须使用强制类型转换，把object强制转换成int类型，即把代码修改为obj = (int)obj + 10;。

            // 但代码中并没有使用强制转换，却也通过了编译。这是因为在第3行，变量i的类型是动态类型，变量的具体类型只能在程序运行时被确定，编译器根本不知道其类型是什么，所以也就不会出现编译错误了。
        }

        // .NET中支持 3种实现动态行为的方式：

        // 1. 使用ExpandoObject来实现动态行为
        public static void ExpandoObjectDemo()
        {
            dynamic expand = new ExpandoObject();
            // 动态绑定成员
            expand.Name = "Learning Hard";
            expand.Age = 24;
            expand.Addmethod = (Func<int, int>)(x => x + 1);
            // 调用类型成员
            Console.WriteLine("expand类型的姓名为：" + expand.Name + " 年龄为： " + expand.Age);
            Console.WriteLine("调用expand类型的动态绑定的方法：" + expand.Addmethod(5));
        }


        public static void DynamicTypeDemo()
        {
            dynamic dynamicobj = new DynamicType();
            dynamicobj.CallMethod();
            dynamicobj.Name = "Learning Hard";
            dynamicobj.Age = "24";
            Console.Read();
        }

        public static void IDynamicMetaObjectProviderDemo()
        {
            dynamic dynamicobj2 = new DynamicType2();
            dynamicobj2.Call();
            Console.Read();
        }
    }

    // 2. 使用DynamicObject来实现动态行为
    class DynamicType : DynamicObject
    {
        // 重写TryXXX方法，该方法表示对对象进行动态调用
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            Console.WriteLine(binder.Name + " 方法正在被调用");
            result = null;
            return true;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            Console.WriteLine(binder.Name + " 属性被设置，" + "设置的值为： " + value);
            return true;
        }
    }

    // 3. 使用IDynamicMetaObjectProvider接口来实现动态行为
    public class DynamicType2 : IDynamicMetaObjectProvider
    {
        public DynamicMetaObject GetMetaObject(Expression parameter)
        {
            Console.WriteLine("开始获得元数据……");
            return new Metadynamic(parameter, this);
        }
    }
    public class Metadynamic : DynamicMetaObject
    {
        internal Metadynamic(Expression expression, DynamicType2 value)
            : base(expression, BindingRestrictions.Empty, value)
        {
        }
        // 重写响应成员调用方法
        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
        {
            // 获得真正的对象
            DynamicType2 target = (DynamicType2)base.Value;
            Expression self = Expression.Convert(base.Expression, typeof(DynamicType2));
            var restrictions = BindingRestrictions.GetInstanceRestriction(self, target);
            // 输出绑定方法名
            Console.WriteLine(binder.Name + " 方法被调用了");
            return new DynamicMetaObject(self, restrictions);
        }
    }

}
